package biz.datalk.industrialland.admin.modules.system.controller;

import biz.datalk.commons.utils.BasicParserUtils;
import biz.datalk.industrialland.common.encrypt.annotation.RSAES;
import biz.datalk.industrialland.common.log.aspect.annotation.SysLog;
import biz.datalk.industrialland.common.log.def.OptEnum;
import biz.datalk.industrialland.common.result.UnifyResult;
import biz.datalk.industrialland.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Random;

/**
 * @author tarofang@163.com
 * @date 2019-09-03
 */
@SuppressWarnings("WeakerAccess")
@Slf4j
@RestController
public class RandCodeImageContrller {

    private static final int COUNT = 200;
    private static final int WIDTH = 105;
    private static final int HEIGHT = 35;
    private static final int LINE_WIDTH = 2;
    private static final int DEFAULT_CAPTCHA_LENGTH = 4;
    /** 备选字符串 */
    private static final String DEFAULT_CAPTCHA_SEED = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static final String CAPTCHA_CODE_PREFIX = "captcha:";
    /** 验证码长度 */
    public static final String SYS_CONFIG_CAPTCHA_LENGTH = "sys.config.captcha.length";
    /** 验证码有效时间 */
    public static final String SYS_CONFIG_CAPTCHA_TIMEOUT = "sys.config.captcha.timeout";
    /** 验证码备选字符串 */
    public static final String SYS_CONFIG_CAPTCHA_SEED = "sys.config.captcha.seed";

    private final RedisUtil redisUtil;

    @Autowired
    public RandCodeImageContrller(RedisUtil redisUtil) {
        this.redisUtil = redisUtil;
    }

    @RSAES
    @PostMapping(value = "/randCodeImage", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @SysLog(OptEnum.LAND_LOGIN_10104)
    public UnifyResult randCodeImage() throws IOException {
        log.debug("randCodeImage into");
        String captchaLenStr = redisUtil.getStr(SYS_CONFIG_CAPTCHA_LENGTH);
        int captchaLen = BasicParserUtils.parseInt(captchaLenStr, DEFAULT_CAPTCHA_LENGTH);
        String captchaSeed = redisUtil.getStr(SYS_CONFIG_CAPTCHA_SEED);
        captchaSeed = StringUtils.isBlank(captchaSeed) ? DEFAULT_CAPTCHA_SEED : captchaSeed.trim();
        final String captcha = genCaptchaCode(captchaSeed, captchaLen);
        log.debug("captcha: {}", captcha);

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            BufferedImage image = generageCaptchaImage(captcha);
            ImageIO.write(image, "JPEG", outputStream);
            outputStream.flush();

            String base64jpg = Base64Utils.encodeToString(outputStream.toByteArray());
            base64jpg = "data:image/jpg;base64," + base64jpg;

            String ran = RandomStringUtils.randomAlphabetic(6);
            long nano = System.nanoTime();
            ran += nano;
            log.debug("captcha ran: {}", ran);
            String key = CAPTCHA_CODE_PREFIX + ran;
            // 过期时间 秒
            String timeoutStr = redisUtil.getStr(SYS_CONFIG_CAPTCHA_TIMEOUT);
            long expire = 60 * BasicParserUtils.parseInt(timeoutStr, 2);
            redisUtil.set(key, captcha, expire);

            return UnifyResult.success().mapData().put("jpg", base64jpg)
                    //.put("captcha", captcha) // 验证码的验证只在后端 前端只需要提示 是否填写
                    .put("ran", ran);
        } finally {
            log.debug("randCodeImage out");
        }
    }

    private BufferedImage generageCaptchaImage(String captcha) {
        // 在内存中创建图象
        final BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

        // 获取图形上下文
        final Graphics2D graphics = (Graphics2D) image.getGraphics();

        // 设定背景颜色
        Color bgColor = getRandColor(180, 240);
        graphics.setColor(bgColor);
        graphics.fillRect(0, 0, WIDTH, HEIGHT);

        // 设定边框颜色
        graphics.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        final Random random = new Random();
        // 随机产生干扰线，使图象中的认证码不易被其它程序探测到
        for (int i = 0; i < COUNT; i++) {
            graphics.setColor(getRandColor(150, 200));

            // 保证画在边框之内
            final int x = random.nextInt(WIDTH - LINE_WIDTH - 1) + 1;
            final int y = random.nextInt(HEIGHT - LINE_WIDTH - 1) + 1;
            final int xl = random.nextInt(LINE_WIDTH);
            final int yl = random.nextInt(LINE_WIDTH);
            graphics.drawLine(x, y, x + xl, y + yl);
        }

        // 绘制验证码
        for (int i = 0, len = captcha.length(); i < len; i++) {
            // 将认证码显示到图象中,调用函数出来的颜色相同，可能是因为种子太接近，所以只能直接生成
            // graphics.setColor(new Color(20 + random.nextInt(130), 20 + random
            // .nextInt(130), 20 + random.nextInt(130)));

            // 设置字体颜色
            graphics.setColor(Color.BLACK);

            // 设置字体样式
            // graphics.setFont(new Font("Arial Black", Font.ITALIC, 18));
            graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));

            // 设置字符，字符间距，上边距
            graphics.drawString(String.valueOf(captcha.charAt(i)), (23 * i) + 8, 26);
        }

        // 图象生效
        graphics.dispose();

        return image;
    }

    private Color getRandColor(int fc, int bc) { // 取得给定范围随机颜色
        final Random random = new Random();
        int max = 255;
        if (fc > max) {
            fc = max;
        }
        if (bc > max) {
            bc = max;
        }

        final int r = fc + random.nextInt(bc - fc);
        final int g = fc + random.nextInt(bc - fc);
        final int b = fc + random.nextInt(bc - fc);

        return new Color(r, g, b);
    }

    @SuppressWarnings("StringBufferMayBeStringBuilder")
    private String genCaptchaCode(String seed, int len) {
        final Random random = new Random();
        final StringBuffer sb = new StringBuffer();
        int length = seed.length();
        for (int i = 0; i < len; i++) {
            sb.append(seed.charAt(random.nextInt(length)));
        }
        return sb.toString();
    }
}